/*
 * Copyright (C) 2015-2017 Alibaba Group Holding Limited
 */

#include <string.h>
#include <stdint.h>
#include <stdio.h>
#include <errno.h>
#include "k_api.h"
#include "ramfs.h"

#define LL_NODE_META_SIZE      (sizeof(ll_node_t *) + sizeof(ll_node_t *))
#define LL_PREV_P_OFFSET(ll_p) (ll_p->n_size)
#define LL_NEXT_P_OFFSET(ll_p) (ll_p->n_size + sizeof(ll_node_t *))

static ramfs_ent_t *ramfs_ent_get(const char *fn);
static ramfs_ent_t *ramfs_ent_new(const char *fn);
static void        *ll_ins_head(ll_t *ll_p);
static void         ll_rem(ll_t *ll_p, void *node_p);
static void        *ll_get_head(ll_t *ll_p);
static void        *ll_get_tail(ll_t *ll_p);
static void        *ll_get_next(ll_t *ll_p, void *n_act);
static void        *ll_get_prev(ll_t *ll_p, void *n_act);
static void         node_set_prev(ll_t *ll_p, ll_node_t *act, ll_node_t *prev);
static void         node_set_next(ll_t *ll_p, ll_node_t *act, ll_node_t *next);
static void         node_set_prev(ll_t *ll_p, ll_node_t *act, ll_node_t *prev);
static void         node_set_next(ll_t *ll_p, ll_node_t *act, ll_node_t *next);
static void         ll_init(ll_t *ll_p, uint32_t n_size);
static ramfs_res_t  create_dir(const char *fn, const void *const_p,
                               uint32_t len);

static ll_t file_ll;
static bool inited = false;

/**
 * Create a driver for ramfs and initialize it.
 */
void ramfs_init(void)
{
    ll_init(&file_ll, sizeof(ramfs_ent_t));

    inited = true;
}

/**
 * Give the state of the ramfs
 * @return true if ramfs is initialized and can be used else false
 */
bool ramfs_ready(void)
{
    return inited;
}

/**
 * Open a file in ramfs
 * @param file_p pointer to a ramfs_file_t variable
 * @param fn name of the file. There are no directories so e.g. "myfile.txt"
 * @param mode element of 'fs_mode_t' enum or its 'OR' connection (e.g.
 * FS_MODE_WR | FS_MODE_RD)
 * @return RAMFS_RES_OK: no error, or any error from ramfs_res_t enum
 */
ramfs_res_t ramfs_open(void *file_p, const char *fn, ramfs_mode_t mode)
{
    ramfs_file_t *fp  = file_p; /*Convert type*/
    ramfs_ent_t  *ent = ramfs_ent_get(fn);

    fp->ent = NULL;

    /* if dir is got return error */
    if ((ent != NULL) && (ent->is_dir == 1)) {
        return RAMFS_RES_NOT_EX;
    }

    /*If the file not exists ...*/
    if (ent == NULL) {
        if ((mode & RAMFS_MODE_WR) !=
            0) { /*Create the file if opened for write*/
            ent = ramfs_ent_new(fn);
            if (ent == NULL) {
                return RAMFS_RES_FULL; /*No space for the new file*/
            }
        } else {
            return RAMFS_RES_NOT_EX; /*Can not read not existing file*/
        }
    }

    /*Can not write already opened and const data files*/
    if ((mode & RAMFS_MODE_WR) != 0) {
        if (ent->oc != 0) {
            return RAMFS_RES_LOCKED;
        }
        if (ent->const_data != 0) {
            return RAMFS_RES_DENIED;
        }
    }

    /*No error, the file can be opened*/
    fp->ent     = ent;
    fp->ent->ar = mode & RAMFS_MODE_RD ? 1 : 0;
    fp->ent->aw = mode & RAMFS_MODE_WR ? 1 : 0;
    fp->rwp     = 0;
    ent->oc++;

    return RAMFS_RES_OK;
}

/**
 * Create a file with a constant data
 * @param fn name of the file (directories are not supported)
 * @param const_p pointer to a constant data
 * @param len length of the data pointed by 'const_p' in bytes
 * @return RAMFS_RES_OK: no error, or any error from ramfs_res_t enum
 */
ramfs_res_t ramfs_create_const(const char *fn, const void *const_p,
                               uint32_t len)
{
    ramfs_file_t file;
    ramfs_res_t  res;

    /*Error if the file already exists*/
    res = ramfs_open(&file, fn, RAMFS_MODE_RD);
    if (res == RAMFS_RES_OK) {
        ramfs_close(&file);
        return RAMFS_RES_DENIED;
    }

    ramfs_close(&file);

    res = ramfs_open(&file, fn, RAMFS_MODE_WR);
    if (res != RAMFS_RES_OK) {
        return res;
    }

    ramfs_ent_t *ent = file.ent;

    if (ent->data_d != NULL) {
        return RAMFS_RES_DENIED;
    }

    ent->data_d     = (void *)const_p;
    ent->size       = len;
    ent->const_data = 1;
    ent->ar         = 1;
    ent->aw         = 0;

    res = ramfs_close(&file);
    if (res != RAMFS_RES_OK) {
        return res;
    }

    return RAMFS_RES_OK;
}

/**
 * Close an opened file
 * @param file_p pointer to an 'ramfs_file_t' variable. (opened with ramfs_open)
 * @return RAMFS_RES_OK: no error, or any error from ramfs_res_t enum
 */
ramfs_res_t ramfs_close(void *file_p)
{
    ramfs_file_t *fp = file_p; /*Convert type*/

    if (fp->ent == NULL) {
        return RAMFS_RES_OK;
    }

    /*Decrement the Open counter*/
    if (fp->ent->oc > 0) {
        fp->ent->oc--;
    }

    return RAMFS_RES_OK;
}

/**
 * Remove a file. The file can not be opened.
 * @param fn '\0' terminated string
 * @return RAMFS_RES_OK: no error, or any error from ramfs_res_t enum
 */
ramfs_res_t ramfs_remove(const char *fn)
{
    ramfs_ent_t *ent = ramfs_ent_get(fn);

    /*Can not be deleted is opened*/
    if (ent->oc != 0) {
        return RAMFS_RES_DENIED;
    }

    ll_rem(&file_ll, ent);
    krhino_mm_free(ent->fn_d);
    ent->fn_d = NULL;
    if (ent->const_data == 0) {
        krhino_mm_free(ent->data_d);
        ent->data_d = NULL;
    }

    krhino_mm_free(ent);

    return RAMFS_RES_OK;
}

/**
 * Read data from an opened file
 * @param file_p pointer to an 'ramfs_file_t' variable. (opened with ramfs_open
 * )
 * @param buf pointer to a memory block where to store the read data
 * @param btr number of Bytes To Read
 * @param br the real number of read bytes (Byte Read)
 * @return RAMFS_RES_OK: no error, or any error from ramfs_res_t enum
 */
ramfs_res_t ramfs_read(void *file_p, void *buf, uint32_t btr, uint32_t *br)
{
    ramfs_file_t *fp = file_p; /*Convert type*/

    ramfs_ent_t *ent = fp->ent;
    *br              = 0;

    if (ent->data_d == NULL || ent->size == 0) { /*Don't read empty files*/
        return RAMFS_RES_OK;
    } else if (fp->ent->ar == 0) { /*The file is not opened for read*/
        return RAMFS_RES_DENIED;
    }

    /*No error, read the file*/
    if (fp->rwp + btr > ent->size) { /*Check too much bytes read*/
        *br = ent->size - fp->rwp;
    } else {
        *br = btr;
    }

    /*Read the data*/
    uint8_t *data8_p;
    if (ent->const_data == 0) {
        data8_p = (uint8_t *)ent->data_d;
    } else {
        data8_p = ent->data_d;
    }

    data8_p += fp->rwp;
    memcpy(buf, data8_p, *br);

    fp->rwp += *br; /*Refresh the read write pointer*/

    return RAMFS_RES_OK;
}

/**
 * Write data to an opened file
 * @param file_p pointer to an 'ramfs_file_t' variable. (opened with ramfs_open)
 * @param buf pointer to a memory block which content will be written
 * @param btw the number Bytes To Write
 * @param bw The real number of written bytes (Byte Written)
 * @return RAMFS_RES_OK: no error, or any error from ramfs_res_t enum
 */
ramfs_res_t ramfs_write(void *file_p, const void *buf, uint32_t btw,
                        uint32_t *bw)
{
    ramfs_file_t *fp = file_p; /*Convert type*/
    *bw              = 0;

    if (fp->ent->aw == 0) {
        return RAMFS_RES_DENIED; /*Not opened for write*/
    }

    ramfs_ent_t *ent = fp->ent;

    /*Reallocate data array if it necessary*/
    uint32_t new_size = fp->rwp + btw;
    if (new_size > ent->size) {
        uint8_t *new_data = krhino_mm_realloc(ent->data_d, new_size);
        if (new_data == NULL) {
            return RAMFS_RES_FULL; /*Cannot allocate the new memory*/
        }

        ent->data_d = new_data;
        ent->size   = new_size;
    }

    /*Write the file*/
    uint8_t *data8_p = (uint8_t *)ent->data_d;
    data8_p += fp->rwp;
    memcpy(data8_p, buf, btw);
    *bw = btw;
    fp->rwp += *bw;

    return RAMFS_RES_OK;
}

/**
 * Set the read write pointer. Also expand the file size if necessary.
 * @param file_p pointer to an 'ramfs_file_t' variable. (opened with ramfs_open
 * )
 * @param pos the new position of read write pointer
 * @return RAMFS_RES_OK: no error, or any error from ramfs_res_t enum
 */
ramfs_res_t ramfs_seek(void *file_p, uint32_t pos)
{
    ramfs_file_t *fp  = file_p; /*Convert type*/
    ramfs_ent_t  *ent = fp->ent;

    /*Simply move the rwp before EOF*/
    if (pos < ent->size) {
        fp->rwp = pos;
    } else { /*Expand the file size*/
        if (fp->ent->aw == 0) {
            return RAMFS_RES_DENIED; /*Not opened for write*/
        }

        uint8_t *new_data = krhino_mm_realloc(ent->data_d, pos);
        if (new_data == NULL) {
            return RAMFS_RES_FULL; /*Out of memory*/
        }

        ent->data_d = new_data;
        ent->size   = pos;
        fp->rwp     = pos;
    }

    return RAMFS_RES_OK;
}

/**
 * Give the position of the read write pointer
 * @param file_p pointer to an 'ramfs_file_t' variable. (opened with ramfs_open
 * )
 * @param pos_p pointer to to store the result
 * @return RAMFS_RES_OK: no error, or any error from ramfs_res_t enum
 */
ramfs_res_t ramfs_tell(void *file_p, uint32_t *pos_p)
{
    ramfs_file_t *fp = file_p; /*Convert type*/

    *pos_p = fp->rwp;

    return RAMFS_RES_OK;
}

/**
 * Truncate the file size to the current position of the read write pointer
 * @param file_p pointer to an 'ramfs_file_t' variable. (opened with ramfs_open
 * )
 * @return RAMFS_RES_OK: no error, or any error from ramfs_res_t enum
 */
ramfs_res_t ramfs_trunc(void *file_p)
{
    ramfs_file_t *fp  = file_p; /*Convert type*/
    ramfs_ent_t  *ent = fp->ent;

    if (fp->ent->aw == 0) {
        return RAMFS_RES_DENIED; /*Not opened for write*/
    }

    void *new_data = krhino_mm_realloc(ent->data_d, fp->rwp);
    if (new_data == NULL) {
        return RAMFS_RES_FULL; /*Out of memory*/
    }

    ent->data_d = new_data;
    ent->size   = fp->rwp;

    return RAMFS_RES_OK;
}

/**
 * Give the size of the file in bytes
 * @param file_p file_p pointer to an 'ramfs_file_t' variable. (opened with
 * ramfs_open )
 * @param size_p pointer to store the size
 * @return RAMFS_RES_OK: no error, or any error from ramfs_res_t enum
 */
ramfs_res_t ramfs_size(void *file_p, uint32_t *size_p)
{
    ramfs_file_t *fp  = file_p; /*Convert type*/
    ramfs_ent_t  *ent = fp->ent;

    *size_p = ent->size;

    return RAMFS_RES_OK;
}

/**
 * get access info
 * @param path The path of the file
 * @param mode the info to get
 * @return RAMFS_RES_OK: no error, or any error from ramfs_res_t enum
 */
ramfs_res_t ramfs_access(const char *path, int mode)
{
    ramfs_ent_t *ent = ramfs_ent_get(path);

    if (mode == F_OK) {
        if (ent == NULL) {
            return RAMFS_RES_DENIED;
        } else {
            return RAMFS_RES_OK;
        }
    }

    if (mode == R_OK) {
        if (ent == NULL) {
            return RAMFS_RES_OK;
        } else {
            if (ent->ar == 1) {
                return RAMFS_RES_OK;
            } else {
                return RAMFS_RES_DENIED;
            }
        }
    }

    if (mode == W_OK) {
        if (ent == NULL) {
            return RAMFS_RES_OK;
        } else {
            if (ent->aw == 1) {
                return RAMFS_RES_OK;
            } else {
                return RAMFS_RES_DENIED;
            }
        }
    }

    if (mode == X_OK) {
        return RAMFS_RES_OK;
    }

    return RAMFS_RES_DENIED;
}

/**
 * Create a directory
 * @param path the path of file
 * @return RAMFS_RES_OK: no error, or any error from ramfs_res_t enum
 */
ramfs_res_t ramfs_mkdir(const char *path)
{
    ramfs_res_t ret = 0;

    ret = create_dir(path, NULL, 0);

    return ret;
}

/**
 * Initialize a ramfs_read_dir_t variable to directory reading
 * @param rddir_p pointer to a 'ramfs_dir_t' variable
 * @param path the path of file
 * @return RAMFS_RES_OK: no error, or any error from ramfs_res_t enum
 */
ramfs_res_t ramfs_dir_open(void *rddir_p, const char *path)
{
    ramfs_dir_t *ramfs_rddir_p = rddir_p;
    ramfs_ent_t *ent           = ramfs_ent_get(path);

    if (ent == NULL) {
        return RAMFS_RES_NOT_EX;
    }

    if (ent->is_dir == 1) {
        ramfs_rddir_p->dir_name = krhino_mm_alloc(strlen(path));

        if (ramfs_rddir_p->dir_name != NULL) {
            strcpy(ramfs_rddir_p->dir_name, path);
        }

        ramfs_rddir_p->last_ent = NULL;

        return RAMFS_RES_OK;
    } else {
        return RAMFS_RES_NOT_EX;
    }
}

/**
 * Read the next file name
 * @param dir_p pointer to an initialized 'ramfs_dir_t' variable
 * @param fn pointer to buffer to sore the file name
 * @return RAMFS_RES_OK: no error, or any error from ramfs_res_t enum
 */
ramfs_res_t ramfs_dir_read(void *dir_p, char *fn)
{
    ramfs_dir_t *ramfs_dir_p = dir_p;
    char        *pname       = NULL;
    char        *pdata       = NULL;
    int          search_flag = 1;
    int          len         = 0;
    int          i           = 0;

    if (ramfs_dir_p->last_ent == NULL) {
        ramfs_dir_p->last_ent = ll_get_head(&file_ll);
    } else {
        ramfs_dir_p->last_ent = ll_get_next(&file_ll, ramfs_dir_p->last_ent);
    }

    while (search_flag == 1) {
        if (ramfs_dir_p->last_ent != NULL) {
            if (strcmp(ramfs_dir_p->dir_name, ramfs_dir_p->last_ent->fn_d) ==
                0) {
                search_flag = 1;
            } else if (strncmp(ramfs_dir_p->dir_name,
                               ramfs_dir_p->last_ent->fn_d,
                               strlen(ramfs_dir_p->dir_name)) != 0) {
                search_flag = 1;
            } else if (*(ramfs_dir_p->last_ent->fn_d +
                         strlen(ramfs_dir_p->dir_name)) != '/') {
                search_flag = 1;
            } else {
                pname = ramfs_dir_p->last_ent->fn_d +
                        strlen(ramfs_dir_p->dir_name) + 1;
                pdata = pname;
                len   = strlen(ramfs_dir_p->last_ent->fn_d) -
                      strlen(ramfs_dir_p->dir_name);

                search_flag = 0;

                for (i = 0; i < len; i++) {
                    if (*pname == '/') {
                        search_flag = 1;
                        break;
                    }
                    pname++;
                }
            }

            if (search_flag == 1) {
                ramfs_dir_p->last_ent =
                  ll_get_next(&file_ll, ramfs_dir_p->last_ent);
            }
        } else {
            search_flag = 0;
        }
    }

    if (ramfs_dir_p->last_ent != NULL) {
        strcpy(fn, pdata);
    } else {
        return RAMFS_RES_NOT_EX;
    }

    return RAMFS_RES_OK;
}

/**
 * Close the directory reading
 * @param rddir_p pointer to an initialized 'ramfs_dir_t' variable
 * @return RAMFS_RES_OK: no error, or any error from ramfs_res_t enum
 */
ramfs_res_t ramfs_dir_close(void *rddir_p)
{
    ramfs_dir_t *ramfs_rddir_p = rddir_p;

    if (ramfs_rddir_p->dir_name != NULL) {
        krhino_mm_free(ramfs_rddir_p->dir_name);
    }

    return RAMFS_RES_OK;
}

/**
 * Get file info
 * @param[in]   path  The path of the file to find information about
 * @param[out]  st    The stat buffer to write to.
 * @return RAMFS_RES_OK: no error, or any error from ramfs_res_t enum
 */
ramfs_res_t ramfs_stat(const char *path, ramfs_stat_t *st)
{
    ramfs_res_t  ret = 0;
    ramfs_ent_t *ent = ramfs_ent_get(path);

    if (st == NULL) {
        return RAMFS_RES_INV_PARAM;
    }

    if (ent != NULL) {
        st->st_size = ent->size;

        if (ent->ar == 1) {
            st->st_mode |= RAMFS_MODE_RD;
        }

        if (ent->aw == 1) {
            st->st_mode |= RAMFS_MODE_WR;
        }

        if (ent->is_dir == 1) {
            st->is_dir = 1;
        }
    } else {
        ret = RAMFS_RES_NOT_EX;
    }

    return ret;
}

/**
 * Give the size of a drive
 * @param total_p pointer to store the total size [kB]
 * @param free_p pointer to store the free site [kB]
 * @return RAMFS_RES_OK: no error, or any error from ramfs_res_t enum
 */
ramfs_res_t ramfs_free(uint32_t *total_p, uint32_t *free_p)
{
    return RAMFS_RES_OK;
}

/**********************
 *   STATIC FUNCTIONS
 **********************/

/**
 * Gives the ramfs_entry from a filename
 * @param fn filename ('\0' terminated string)
 * @return pointer to the dynamically allocated entry with 'fn' filename.
 *         NULL if no entry found with that name.
 */
static ramfs_ent_t *ramfs_ent_get(const char *fn)
{
    ramfs_ent_t *fp;

    LL_READ(file_ll, fp)
    {
        if (strcmp(fp->fn_d, fn) == 0) {
            return fp;
        }
    }

    return NULL;
}

/**
 * Create a new entry with 'fn' filename
 * @param fn filename ('\0' terminated string)
 * @return pointer to the dynamically allocated new entry.
 *         NULL if no space for the entry.
 */
static ramfs_ent_t *ramfs_ent_new(const char *fn)
{
    ramfs_ent_t *new_ent = NULL;
    new_ent              = ll_ins_head(&file_ll); /*Create a new file*/
    if (new_ent == NULL) {
        return NULL;
    }

    new_ent->fn_d = krhino_mm_alloc(strlen(fn) + 1); /*Save the name*/
    strcpy(new_ent->fn_d, fn);
    new_ent->data_d     = NULL;
    new_ent->size       = 0;
    new_ent->oc         = 0;
    new_ent->const_data = 0;
    new_ent->is_dir     = 0;

    return new_ent;
}

/**
 * Create a create_dir
 * @param fn name of the file (directories are not supported)
 * @param const_p pointer to a constant data
 * @param len length of the data pointed by 'const_p' in bytes
 * @return RAMFS_RES_OK: no error, or any error from ramfs_res_t enum
 */
ramfs_res_t create_dir(const char *fn, const void *const_p, uint32_t len)
{
    ramfs_file_t file;
    ramfs_res_t  res;

    /*Error if the file already exists*/
    res = ramfs_open(&file, fn, RAMFS_MODE_RD);
    if (res == RAMFS_RES_OK) {
        ramfs_close(&file);
        return RAMFS_RES_DENIED;
    }

    ramfs_close(&file);

    res = ramfs_open(&file, fn, RAMFS_MODE_WR);
    if (res != RAMFS_RES_OK) {
        return res;
    }

    ramfs_ent_t *ent = file.ent;

    if (ent->data_d != NULL) {
        return RAMFS_RES_DENIED;
    }

    ent->data_d = (void *)const_p;
    ent->size   = len;
    ent->is_dir = 1;
    ent->ar     = 1;
    ent->aw     = 1;

    res = ramfs_close(&file);
    if (res != RAMFS_RES_OK) {
        return res;
    }

    return RAMFS_RES_OK;
}

/**
 * Initialize linked list
 * @param ll_dsc pointer to ll_dsc variable
 * @param n_size the size of 1 node in bytes
 */
void ll_init(ll_t *ll_p, uint32_t n_size)
{
    ll_p->head = NULL;
    ll_p->tail = NULL;

    if (n_size & 0x3) {
        n_size &= ~0x3;
        n_size += 4;
    }

    ll_p->n_size = n_size;
}

/**
 * Add a new head to a linked list
 * @param ll_p pointer to linked list
 * @return pointer to the new head
 */
void *ll_ins_head(ll_t *ll_p)
{
    ll_node_t *n_new;

    n_new = krhino_mm_alloc(ll_p->n_size + LL_NODE_META_SIZE);

    if (n_new != NULL) {
        node_set_prev(ll_p, n_new, NULL);       /*No prev. before the new head*/
        node_set_next(ll_p, n_new, ll_p->head); /*After new comes the old head*/

        if (ll_p->head !=
            NULL) { /*If there is old head then before it goes the new*/
            node_set_prev(ll_p, ll_p->head, n_new);
        }

        ll_p->head = n_new; /*Set the new head in the dsc.*/
        if (ll_p->tail ==
            NULL) { /*If there is no tail (1. node) set the tail too*/
            ll_p->tail = n_new;
        }
    }

    return n_new;
}

/**
 * Add a new tail to a linked list
 * @param ll_p pointer to linked list
 * @return pointer to the new tail
 */
void *ll_ins_tail(ll_t *ll_p)
{
    ll_node_t *n_new;

    n_new = krhino_mm_alloc(ll_p->n_size + LL_NODE_META_SIZE);

    if (n_new != NULL) {
        node_set_next(ll_p, n_new, NULL); /*No next after the new tail*/
        node_set_prev(ll_p, n_new,
                      ll_p->tail); /*The prev. before new is tho old tail*/
        if (ll_p->tail !=
            NULL) { /*If there is old tail then the new comes after it*/
            node_set_next(ll_p, ll_p->tail, n_new);
        }

        ll_p->tail = n_new; /*Set the new tail in the dsc.*/
        if (ll_p->head ==
            NULL) { /*If there is no head (1. node) set the head too*/
            ll_p->head = n_new;
        }
    }

    return n_new;
}


/**
 * Remove the node 'node_p' from 'll_p' linked list.
 * It Dose not free the the memory of node.
 * @param ll_p pointer to the linked list of 'node_p'
 * @param node_p pointer to node in 'll_p' linked list
 */
void ll_rem(ll_t *ll_p, void *node_p)
{
    if (ll_get_head(ll_p) == node_p) {
        /*The new head will be the node after 'n_act'*/
        ll_p->head = ll_get_next(ll_p, node_p);
        if (ll_p->head == NULL) {
            ll_p->tail = NULL;
        } else {
            node_set_prev(ll_p, ll_p->head, NULL);
        }
    } else if (ll_get_tail(ll_p) == node_p) {
        /*The new tail will be the  node before 'n_act'*/
        ll_p->tail = ll_get_prev(ll_p, node_p);
        if (ll_p->tail == NULL) {
            ll_p->head = NULL;
        } else {
            node_set_next(ll_p, ll_p->tail, NULL);
        }
    } else {
        ll_node_t *n_prev = ll_get_prev(ll_p, node_p);
        ll_node_t *n_next = ll_get_next(ll_p, node_p);

        node_set_next(ll_p, n_prev, n_next);
        node_set_prev(ll_p, n_next, n_prev);
    }
}

/**
 * Remove and free all elements from a linked list. The list remain valid but
 * become empty.
 * @param ll_p pointer to linked list
 */
void ll_clear(ll_t *ll_p)
{
    void *i;
    void *i_next;

    i      = ll_get_head(ll_p);
    i_next = NULL;

    while (i != NULL) {
        i_next = ll_get_next(ll_p, i);

        ll_rem(ll_p, i);
        krhino_mm_free(i);

        i = i_next;
    }
}

/**
 * Move a node to a new linked list
 * @param ll_ori_p pointer to the original (old) linked list
 * @param ll_new_p pointer to the new linked list
 * @param node pointer to a node
 */
void ll_chg_list(ll_t *ll_ori_p, ll_t *ll_new_p, void *node)
{
    ll_rem(ll_ori_p, node);

    /*Set node as head*/
    node_set_prev(ll_new_p, node, NULL);
    node_set_next(ll_new_p, node, ll_new_p->head);

    if (ll_new_p->head !=
        NULL) { /*If there is old head then before it goes the new*/
        node_set_prev(ll_new_p, ll_new_p->head, node);
    }

    ll_new_p->head = node; /*Set the new head in the dsc.*/
    if (ll_new_p->tail ==
        NULL) { /*If there is no tail (first node) set the tail too*/
        ll_new_p->tail = node;
    }
}

/**
 * Return with head node of the linked list
 * @param ll_p pointer to linked list
 * @return pointer to the head of 'll_p'
 */
void *ll_get_head(ll_t *ll_p)
{
    void *head = NULL;

    if (ll_p != NULL) {
        head = ll_p->head;
    }

    return head;
}

/**
 * Return with tail node of the linked list
 * @param ll_p pointer to linked list
 * @return pointer to the head of 'll_p'
 */
void *ll_get_tail(ll_t *ll_p)
{
    void *tail = NULL;

    if (ll_p != NULL) {
        tail = ll_p->tail;
    }

    return tail;
}

/**
 * Return with the pointer of the next node after 'n_act'
 * @param ll_p pointer to linked list
 * @param n_act pointer a node
 * @return pointer to the next node
 */
void *ll_get_next(ll_t *ll_p, void *n_act)
{
    void *next = NULL;

    if (ll_p != NULL) {
        ll_node_t *n_act_d = n_act;
        memcpy(&next, n_act_d + LL_NEXT_P_OFFSET(ll_p), sizeof(void *));
    }

    return next;
}

/**
 * Return with the pointer of the previous node after 'n_act'
 * @param ll_p pointer to linked list
 * @param n_act pointer a node
 * @return pointer to the previous node
 */
void *ll_get_prev(ll_t *ll_p, void *n_act)
{
    void *prev = NULL;

    if (ll_p != NULL) {
        ll_node_t *n_act_d = n_act;
        memcpy(&prev, n_act_d + LL_PREV_P_OFFSET(ll_p), sizeof(void *));
    }

    return prev;
}

void ll_swap(ll_t *ll_p, void *n1_p, void *n2_p)
{
    (void)(ll_p);
    (void)(n1_p);
    (void)(n2_p);
    /*TODO*/
}

/**********************
 *   STATIC FUNCTIONS
 **********************/

/**
 * Set the 'pervious node pointer' of a node
 * @param ll_p pointer to linked list
 * @param act pointer to a node which prev. node pointer should be set
 * @param prev pointer to a node which should be the previous node before 'act'
 */
static void node_set_prev(ll_t *ll_p, ll_node_t *act, ll_node_t *prev)
{
    memcpy(act + LL_PREV_P_OFFSET(ll_p), &prev, sizeof(ll_node_t *));
}

/**
 * Set the 'next node pointer' of a node
 * @param ll_p pointer to linked list
 * @param act pointer to a node which next node pointer should be set
 * @param next pointer to a node which should be the next node before 'act'
 */
static void node_set_next(ll_t *ll_p, ll_node_t *act, ll_node_t *next)
{
    memcpy(act + LL_NEXT_P_OFFSET(ll_p), &next, sizeof(ll_node_t *));
}
